/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "message_builder.h"

#include "hilog_wrapper.h"
#include "spatial_awareness_common.h"

namespace OHOS {
namespace Msdp {
MessageBuilder::MessageBuilder()
{
    request_ = std::make_shared<SpatialRequest>();
    response_ = std::make_shared<SpatialResponse>();
}

MessageBuilder::~MessageBuilder()
{
}

void MessageBuilder::BuildMapJson(std::shared_ptr<SpatialMap> map, Json::Value& root)
{
    try {
        Json::Value data;
        Json::Value center;
        Json::Value mapJson;
        Json::Value history;
        Json::Value commitMsg;
        if (map == nullptr) {
            return;
        }
        center["dev_id"] = map->center.deviceId;
        center["name"] = map->center.deviceName;
        center["net_id"] = map->center.networkId;
        center["dev_t"] = map->center.typeId;
        data["center"]  = center;
        int i = 0;
        for (auto iter = map->map.begin(); iter != map->map.end(); iter++) {
            Json::Value coordinate;
            BuildMapJsonCoordinate(coordinate, iter->first, iter->second);
            mapJson[i] = coordinate;
        }
        data["map"] = mapJson;
        commitMsg["top"] = map->top;
        commitMsg["cmt"] = map->committed;
        SPATIAL_LOGI(MODULE_SERVICE, "insert top cmt in json");
        commitMsg["ct"] = static_cast<uint64_t>(map->commitTime);
        commitMsg["ut"] = static_cast<uint64_t>(map->updateTime);
        data["commit"] = commitMsg;
        i = 0;
        for (auto iter = map->history.begin(); iter != map->history.end(); iter++) {
            Json::Value commit;
            commit["c"] = iter->first;
            commit["u"] = static_cast<uint64_t>(iter->second);
            history[i] = commit;
        }
        data["his"] = history;
        root["data"] = data;
    } catch (Json::RuntimeError &rt) {
        SPATIAL_LOGE(MODULE_SERVICE, "MessageBuilder RuntimeError %{public}s", rt.what());
    } catch (Json::LogicError &lt) {
        SPATIAL_LOGE(MODULE_SERVICE, "MessageBuilder LogicError %{public}s", lt.what());
    }
}

void MessageBuilder::BuildMapJsonCoordinate(Json::Value &jsCoordinate, const DeviceInfo &info,
    const DeviceCoordinate &coordinate)
{
    jsCoordinate["dev_id"] = info.deviceId;
    jsCoordinate["name"] = info.deviceName;
    jsCoordinate["net_id"] = info.networkId;
    jsCoordinate["dev_t"] = info.typeId;

    jsCoordinate["dis"] = coordinate.distance;
    jsCoordinate["ang"] = coordinate.angle;
    jsCoordinate["exa"] = coordinate.exact;
    jsCoordinate["x"] = coordinate.x;
    jsCoordinate["y"] = coordinate.y;
    jsCoordinate["kt"] = static_cast<uint64_t>(coordinate.keepingTime);
    jsCoordinate["ut"] = static_cast<uint64_t>(coordinate.updateTime);
    jsCoordinate["io"] = coordinate.innerouter;
    jsCoordinate["exist"] = coordinate.exist;
}

std::string MessageBuilder::BuildStringForRequest(std::shared_ptr<SpatialRequest> request)
{
    Json::Value root;
    Json::StreamWriterBuilder writer;
    root["act"] = "request";
    switch (request->requestId) {
        case REQUEST_GET_MAP:
            root["msg"] = "get_map";
            root["type"] = "none";
            break;
        case REQUEST_SYNC_MAP_OFFER:
            root["msg"] = "sync_map_offer";
            root["type"] = "int";
            root["data"] = request->value;
            break;
        case REQUEST_SYNC_MAP:
            root["msg"] = "sync_map";
            root["type"] = "map";
            BuildMapJson(request->map, root);
            break;
        default:
            break;
    }
    return Json::writeString(writer, root);
}

std::string MessageBuilder::BuildStringForResponse(std::shared_ptr<SpatialResponse> response)
{
    Json::Value root;
    Json::StreamWriterBuilder writer;
    root["act"] = "response";
    root["status"] = response->status;
    switch (response->responseId) {
        case RESPONSE_GET_MAP:
            root["msg"] = "get_map";
            root["type"] = "map";
            BuildMapJson(response->map, root);
            break;
        case RESPONSE_SYNC_MAP_OFFER:
            root["msg"] = "sync_map_offer";
            root["type"] = "none";
            break;
        case RESPONSE_SYNC_MAP:
            root["msg"] = "sync_map";
            root["type"] = "none";
            break;
        default:
            break;
    }
    return Json::writeString(writer, root);
}

MessageBuilder::MessageBuilder(std::shared_ptr<SpatialRequest> request)
{
    type_ = TYPE_REQUEST;
    request_ = request;

    messageStr_ = BuildStringForRequest(request_);
    SPATIAL_LOGI(MODULE_SERVICE, "messageStr_: %{public}s", messageStr_.c_str());
}

MessageBuilder::MessageBuilder(std::shared_ptr<SpatialResponse> response)
{
    type_ = TYPE_RESPONSE;
    response_ = response;
    messageStr_ = BuildStringForResponse(response_);
}

std::shared_ptr<SpatialMap> MessageBuilder::BuildMapObject(Json::Value& data)
{
    try {
        std::shared_ptr<SpatialMap> spatialMap = std::make_shared<SpatialMap>();
        Json::Value center = data["center"];
        spatialMap->center.deviceId = center["dev_id"].asString();
        spatialMap->center.networkId = center["net_id"].asString();
        spatialMap->center.deviceName = center["name"].asString();
        spatialMap->center.typeId = center["dev_t"].asInt();
        Json::Value map = data["map"];
        for (unsigned int i = 0; i < map.size(); i++) {
            DeviceInfo device;
            device.deviceId = map[i]["dev_id"].asString();
            device.networkId = map[i]["net_id"].asString();
            device.deviceName = map[i]["name"].asString();
            device.typeId = map[i]["dev_t"].asInt();
            DeviceCoordinate coordinate;
            coordinate.distance = map[i]["dis"].asFloat();
            coordinate.angle = map[i]["ang"].asFloat();
            coordinate.exact = map[i]["exa"].asBool();
            coordinate.x = map[i]["x"].asFloat();
            coordinate.y = map[i]["y"].asFloat();
            coordinate.keepingTime = map[i]["kt"].asUInt();
            coordinate.updateTime = map[i]["ut"].asUInt64();
            coordinate.innerouter = map[i]["io"].asInt();
            coordinate.exist = map[i]["exist"].asInt();
            spatialMap->map.emplace(device, coordinate);
        }
        Json::Value commit = data["commit"];
        spatialMap->top = commit["top"].asUInt();
        spatialMap->committed = commit["cmt"].asBool();
        spatialMap->commitTime = commit["ct"].asUInt64();
        spatialMap->updateTime = commit["ut"].asUInt64();
        Json::Value history = data["his"];
        for (unsigned int i = 0; i < history.size(); i++) {
            uint32_t commit = history[i]["c"].asUInt();
            time_t time = history[i]["u"].asUInt64();
            spatialMap->history.emplace(commit, time);
        }
        return spatialMap;
    } catch (Json::RuntimeError &rt) {
        SPATIAL_LOGE(MODULE_SERVICE, "BuildMapObject RuntimeError %{public}s", rt.what());
        return nullptr;
    } catch (Json::LogicError &lt) {
        SPATIAL_LOGE(MODULE_SERVICE, "BuildMapObject LogicError %{public}s", lt.what());
        return nullptr;
    }
}

void MessageBuilder::BuildRequest(Json::Value& root, int sessionId, std::shared_ptr<SpatialRequest> request)
{
    std::string msg = root["msg"].asString();
    if (msg == "get_map") {
        request->requestId = REQUEST_GET_MAP;
    } else if (msg == "sync_map_offer") {
        request->requestId = REQUEST_SYNC_MAP_OFFER;
        if (root["type"].asString() == "int") {
            request->value = root["data"].asUInt();
        }
    } else if (msg == "sync_map") {
        request->requestId = REQUEST_SYNC_MAP;
        if (root["type"].asString() == "map") {
            SPATIAL_LOGI(MODULE_SERVICE, "BuildRequest type");
            request->map = BuildMapObject(root["data"]);
        }
    }
    request->sessionId = sessionId;
}

void MessageBuilder::BuildResponse(Json::Value& root, int sessionId, std::shared_ptr<SpatialResponse> response)
{
    SPATIAL_LOGI(MODULE_SERVICE, "BuildResponse enter");
    std::string msg = root["msg"].asString();
    response->status = root["status"].asUInt();
    if (msg == "get_map") {
        SPATIAL_LOGI(MODULE_SERVICE, "BuildResponse Get map");
        response->responseId = RESPONSE_GET_MAP;
        if (root["type"].asString() == "map") {
            SPATIAL_LOGI(MODULE_SERVICE, "BuildResponse type");
            response->map = BuildMapObject(root["data"]);
        }
    } else if (msg == "sync_map_offer") {
        response->responseId = RESPONSE_SYNC_MAP_OFFER;
    } else if (msg == "sync_map") {
        response->responseId = RESPONSE_SYNC_MAP;
    }
    response->sessionId = sessionId;
}

MessageBuilder::MessageBuilder(int sessionId, std::string& str)
{
    try {
        SPATIAL_LOGI(MODULE_SERVICE, "MessageBuilder parse str");
        Json::CharReaderBuilder builder;
        Json::CharReader* reader = builder.newCharReader();
        Json::Value root;
        std::string err;
        bool ret = reader->parse(str.begin().base(), str.end().base(), &root, &err);
        if (!ret) {
            SPATIAL_LOGE(MODULE_SERVICE,
                "Can't parse string err: %{public}s", err.c_str());
            return;
        }
        std::string act = root["act"].asString();
        if (act == "request") {
            type_ = TYPE_REQUEST;
            request_ = std::make_shared<SpatialRequest>();
            SPATIAL_LOGI(MODULE_SERVICE, "MessageBuilder request");
            BuildRequest(root, sessionId, request_);
        } else {
            type_ = TYPE_RESPONSE;
            response_ = std::make_shared<SpatialResponse>();
            SPATIAL_LOGI(MODULE_SERVICE, "MessageBuilder response");
            BuildResponse(root, sessionId, response_);
        }
    } catch (Json::RuntimeError &rt) {
        SPATIAL_LOGE(MODULE_SERVICE,
            "MessageBuilder RuntimeError %{public}s", rt.what());
    } catch (Json::LogicError &lt) {
        SPATIAL_LOGE(MODULE_SERVICE,
            "MessageBuilder LogicError %{public}s", lt.what());
    }
}

bool MessageBuilder::IsRequest()
{
    return (type_ == TYPE_REQUEST);
}

bool MessageBuilder::IsResponse()
{
    return (type_ == TYPE_RESPONSE);
}

std::shared_ptr<SpatialRequest> MessageBuilder::GetRequest()
{
    return request_;
}

std::shared_ptr<SpatialResponse> MessageBuilder::GetResponse()
{
    return response_;
}

std::string MessageBuilder::GetString()
{
    SPATIAL_LOGI(MODULE_SERVICE, "messageStr_: %{public}s, size: %{public}d", messageStr_.c_str(), messageStr_.size());
    return messageStr_;
}
} // namespace Msdp
} // namespace OHOS